"""
文件相关的数据模型
对应数据库表: base_file
"""
import hashlib
import mimetypes
from datetime import datetime
from typing import Optional

from pydantic import BaseModel, Field, validator, ConfigDict


class FileBase(BaseModel):
    """
    文件基础模型
    """
    biz_type: Optional[str] = Field(None, description="业务类型，如DEVICE、WORK_ORDER等", max_length=32)
    biz_id: Optional[str] = Field(None, description="业务ID", max_length=64)
    file_name: str = Field(..., description="原始文件名", max_length=255)
    file_ext: Optional[str] = Field(None, description="文件扩展名", max_length=8)
    file_size: Optional[int] = Field(None, description="文件大小（字节）", ge=0)
    mime_type: Optional[str] = Field(None, description="MIME类型", max_length=64)
    store_path: Optional[str] = Field(None, description="存储路径", max_length=512)
    url: Optional[str] = Field(None, description="访问URL", max_length=512)
    md5: Optional[str] = Field(None, description="文件MD5值", max_length=32)
    status: int = Field(1, description="状态：1有效，0无效", ge=0, le=1)
    creator_id: Optional[int] = Field(None, description="上传用户ID")

    @validator('file_name')
    def validate_file_name(cls, v):
        """验证文件名"""
        if not v or not v.strip():
            raise ValueError("文件名不能为空")
        # 检查非法字符
        invalid_chars = '<>:"/\\|?*'
        if any(char in v for char in invalid_chars):
            raise ValueError(f"文件名不能包含非法字符: {invalid_chars}")
        return v.strip()

    @validator('file_ext', pre=True)
    def validate_file_ext(cls, v, values):
        """验证文件扩展名"""
        if v is None and 'file_name' in values:
            # 从文件名提取扩展名
            file_name = values['file_name']
            if '.' in file_name:
                ext = file_name.rsplit('.', 1)[1].lower()
                return ext[:8]  # 限制长度
        if v:
            return v.lower().lstrip('.')
        return v

    @validator('mime_type', pre=True)
    def validate_mime_type(cls, v, values):
        """验证MIME类型"""
        if v is None and 'file_name' in values:
            # 从文件名推断MIME类型
            file_name = values['file_name']
            mime_type, _ = mimetypes.guess_type(file_name)
            return mime_type
        return v

    @validator('md5')
    def validate_md5(cls, v):
        """验证MD5值格式"""
        if v and len(v) != 32:
            raise ValueError("MD5值必须是32位十六进制字符串")
        if v and not all(c in '0123456789abcdef' for c in v.lower()):
            raise ValueError("MD5值必须是十六进制字符串")
        return v.lower() if v else v

    @validator('file_size')
    def validate_file_size(cls, v):
        """验证文件大小"""
        if v is not None and v < 0:
            raise ValueError("文件大小不能为负数")
        # 限制最大文件大小为100MB
        max_size = 100 * 1024 * 1024  # 100MB
        if v is not None and v > max_size:
            raise ValueError(f"文件大小不能超过{max_size // (1024 * 1024)}MB")
        return v


class File(FileBase):
    """
    文件完整模型（包含数据库字段）
    """
    id: int = Field(..., description="主键ID")
    create_time: datetime = Field(..., description="创建时间")
    update_time: datetime = Field(..., description="更新时间")

    model_config = ConfigDict(from_attributes=True)


class FileUploadResult(BaseModel):
    """
    文件上传结果模型
    """
    id: int = Field(..., description="文件ID")
    url: str = Field(..., description="访问URL")
    name: str = Field(..., description="文件名")
    size: Optional[int] = Field(None, description="文件大小")
    ext: Optional[str] = Field(None, description="文件扩展名")
    md5: Optional[str] = Field(None, description="文件MD5")

    @classmethod
    def from_file(cls, file: File) -> "FileUploadResult":
        """从文件模型转换为上传结果"""
        return cls(
            id=file.id,
            url=file.url or "",
            name=file.file_name,
            size=file.file_size,
            ext=file.file_ext,
            md5=file.md5
        )


class FileInfo(BaseModel):
    """
    文件信息模型（用于列表展示）
    """
    id: int = Field(..., description="文件ID")
    file_name: str = Field(..., description="文件名")
    file_ext: Optional[str] = Field(None, description="文件扩展名")
    file_size: Optional[int] = Field(None, description="文件大小")
    mime_type: Optional[str] = Field(None, description="MIME类型")
    url: Optional[str] = Field(None, description="访问URL")
    create_time: datetime = Field(..., description="上传时间")
    creator_id: Optional[int] = Field(None, description="上传用户ID")

    @property
    def file_size_mb(self) -> Optional[float]:
        """返回以MB为单位的文件大小"""
        if self.file_size is None:
            return None
        return round(self.file_size / (1024 * 1024), 2)

    @property
    def is_image(self) -> bool:
        """判断是否为图片文件"""
        if not self.mime_type:
            return False
        return self.mime_type.startswith('image/')

    @property
    def is_document(self) -> bool:
        """判断是否为文档文件"""
        if not self.mime_type:
            return False
        doc_types = [
            'application/pdf',
            'application/msword',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'application/vnd.ms-excel',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'text/plain',
            'text/csv'
        ]
        return self.mime_type in doc_types

    model_config = ConfigDict(from_attributes=True)


class FileFilter(BaseModel):
    """
    文件查询过滤器
    """
    biz_type: Optional[str] = Field(None, description="业务类型")
    biz_id: Optional[str] = Field(None, description="业务ID")
    creator_id: Optional[int] = Field(None, description="上传用户ID")
    file_ext: Optional[str] = Field(None, description="文件扩展名")
    mime_type: Optional[str] = Field(None, description="MIME类型")
    status: Optional[int] = Field(None, description="文件状态")
    keyword: Optional[str] = Field(None, description="文件名关键词搜索")


class FileStatistics(BaseModel):
    """
    文件统计信息
    """
    total_count: int = Field(..., description="文件总数")
    total_size: int = Field(..., description="文件总大小（字节）")
    image_count: int = Field(0, description="图片文件数")
    document_count: int = Field(0, description="文档文件数")
    other_count: int = Field(0, description="其他文件数")

    @property
    def total_size_mb(self) -> float:
        """返回以MB为单位的总大小"""
        return round(self.total_size / (1024 * 1024), 2)


def calculate_file_md5(content: bytes) -> str:
    """
    计算文件内容的MD5值

    Args:
        content: 文件内容字节数据

    Returns:
        str: MD5十六进制字符串
    """
    return hashlib.md5(content).hexdigest()


def get_file_icon_by_type(mime_type: Optional[str]) -> str:
    """
    根据MIME类型返回文件图标

    Args:
        mime_type: MIME类型

    Returns:
        str: 图标名称
    """
    if not mime_type:
        return "file"

    if mime_type.startswith("image/"):
        return "image"
    elif mime_type.startswith("video/"):
        return "video"
    elif mime_type.startswith("audio/"):
        return "audio"
    elif mime_type == "application/pdf":
        return "pdf"
    elif "word" in mime_type or "msword" in mime_type:
        return "word"
    elif "excel" in mime_type or "sheet" in mime_type:
        return "excel"
    elif "powerpoint" in mime_type or "presentation" in mime_type:
        return "powerpoint"
    elif mime_type in ["application/zip", "application/x-rar-compressed", "application/x-7z-compressed"]:
        return "archive"
    elif mime_type.startswith("text/"):
        return "text"
    else:
        return "file"
